package com.wyx.SpringSecurityConfig;


import com.wyx.SpringSecurityConfig.mapper.SecurityUserMapper;
import com.wyx.SpringSecurityConfig.pojo.SecurityUser;
import com.wyx.utils.PasswordEncryption;
import com.wyx.utils.TokenManager;
import lombok.extern.java.Log;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Log
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {


    private final RedisTemplate redisTemplate;

    private final SecurityUserMapper securityUserMapper;

    private final AuthenticationManager authenticationManager;


    public TokenLoginFilter(AuthenticationManager authenticationManager, RedisTemplate redisTemplate, SecurityUserMapper securityUserMapper) {
        this.redisTemplate = redisTemplate;
        this.authenticationManager = authenticationManager;
        this.securityUserMapper = securityUserMapper;
        this.setPostOnly(false);
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
    }


    //1 获取表单提交用户名和密码
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        //获取表单提交数据
        //使用流方式获取表单信息
        //Users user = new ObjectMapper().readValue(request.getInputStream(), Users.class);
        //User users = new User(request.getParameter("username"), request.getParameter("password"));
        // 设置盐，从数据库中获取
        SecurityUser user = securityUserMapper.getSecurityUserByUserName(request.getParameter("username"));
        // 将密码修改为用户输入的
        user.setPassword(request.getParameter("password"));
        // 将加密的盐保存到请求中
        request.setAttribute("loginSalt",user.getSalt());
        return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(),
                PasswordEncryption.encryption(user.getPassword(),user.getSalt()),
                new ArrayList<>()));
    }
    //2 认证成功调用的方法
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response, FilterChain chain, Authentication authResult) {
        //认证成功，得到认证成功之后用户信息
        User user = (User) authResult.getPrincipal();
        //查询数据库获取用户ID 用户名，用户权限。
        String username = user.getUsername();

        StringBuilder roles = new StringBuilder();
        for (GrantedAuthority authority : authResult.getAuthorities()) {
            roles.append(authority);
            roles.append(",");
        }
        // 消除最后一个分号
        if (roles.length()>=1){
            roles.replace(roles.length()-1,roles.length(),"");
        }
        String token = TokenManager.encryption(Long.valueOf(new Random().nextInt()),username, String.valueOf(roles));
        //把用户名称和用户权限列表放到redis，并设置过期时间
        HashMap tokenMap = TokenManager.jwtDecrypt(token);
        Date date = (Date) tokenMap.get("expireTime");
        redisTemplate.opsForValue().set(username,token,(date.getTime()-new Date().getTime())/1000, TimeUnit.SECONDS);
        //返回token
        response.setHeader("token",token);

    }

    //3 认证失败调用的方法
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws ServletException, IOException {
        request.getRequestDispatcher("/authority/AuthenticationFailed").forward(request,response);
        log.warning("认证失败，请重新认证");
    }

}
